<?php
/*
 * @ https://EasyToYou.eu - IonCube v11 Decoder Online
 * @ PHP 7.2 & 7.3
 * @ Decoder version: 1.1.6
 * @ Release: 10/08/2022
 */

// Decoded file for php version 71.
class PHPExcel_Writer_Excel5_Parser
{
    public $currentCharacter;
    public $currentToken;
    private $formula;
    public $lookAhead;
    private $parseTree;
    private $externalSheets;
    public $references;
    const REGEX_SHEET_TITLE_UNQUOTED = "[^\\*\\:\\/\\\\\\?\\[\\]\\+\\-\\% \\'\\^\\&\\<\\>\\=\\,\\;\\#\\(\\)\\\"\\{\\}]+";
    const REGEX_SHEET_TITLE_QUOTED = "(([^\\*\\:\\/\\\\\\?\\[\\]\\'])+|(\\'\\')+)+";
    public function __construct()
    {
        $this->currentCharacter = 0;
        $this->currentToken = "";
        $this->formula = "";
        $this->lookAhead = "";
        $this->parseTree = "";
        $this->initializeHashes();
        $this->externalSheets = [];
        $this->references = [];
    }
    private function initializeHashes()
    {
        $this->ptg = ["ptgExp" => 1, "ptgTbl" => 2, "ptgAdd" => 3, "ptgSub" => 4, "ptgMul" => 5, "ptgDiv" => 6, "ptgPower" => 7, "ptgConcat" => 8, "ptgLT" => 9, "ptgLE" => 10, "ptgEQ" => 11, "ptgGE" => 12, "ptgGT" => 13, "ptgNE" => 14, "ptgIsect" => 15, "ptgUnion" => 16, "ptgRange" => 17, "ptgUplus" => 18, "ptgUminus" => 19, "ptgPercent" => 20, "ptgParen" => 21, "ptgMissArg" => 22, "ptgStr" => 23, "ptgAttr" => 25, "ptgSheet" => 26, "ptgEndSheet" => 27, "ptgErr" => 28, "ptgBool" => 29, "ptgInt" => 30, "ptgNum" => 31, "ptgArray" => 32, "ptgFunc" => 33, "ptgFuncVar" => 34, "ptgName" => 35, "ptgRef" => 36, "ptgArea" => 37, "ptgMemArea" => 38, "ptgMemErr" => 39, "ptgMemNoMem" => 40, "ptgMemFunc" => 41, "ptgRefErr" => 42, "ptgAreaErr" => 43, "ptgRefN" => 44, "ptgAreaN" => 45, "ptgMemAreaN" => 46, "ptgMemNoMemN" => 111, "ptgNameX" => 57, "ptgRef3d" => 58, "ptgArea3d" => 59, "ptgRefErr3d" => 60, "ptgAreaErr3d" => 125, "ptgArrayV" => 64, "ptgFuncV" => 65, "ptgFuncVarV" => 66, "ptgNameV" => 67, "ptgRefV" => 68, "ptgAreaV" => 69, "ptgMemAreaV" => 70, "ptgMemErrV" => 71, "ptgMemNoMemV" => 72, "ptgMemFuncV" => 73, "ptgRefErrV" => 74, "ptgAreaErrV" => 75, "ptgRefNV" => 76, "ptgAreaNV" => 77, "ptgMemAreaNV" => 78, "ptgFuncCEV" => 88, "ptgNameXV" => 89, "ptgRef3dV" => 90, "ptgArea3dV" => 91, "ptgRefErr3dV" => 92, "ptgArrayA" => 96, "ptgFuncA" => 97, "ptgFuncVarA" => 98, "ptgNameA" => 99, "ptgRefA" => 100, "ptgAreaA" => 101, "ptgMemAreaA" => 102, "ptgMemErrA" => 103, "ptgMemNoMemA" => 104, "ptgMemFuncA" => 105, "ptgRefErrA" => 106, "ptgAreaErrA" => 107, "ptgRefNA" => 108, "ptgAreaNA" => 109, "ptgMemAreaNA" => 110, "ptgFuncCEA" => 120, "ptgNameXA" => 121, "ptgRef3dA" => 122, "ptgArea3dA" => 123, "ptgRefErr3dA" => 124];
        $this->functions = ["COUNT" => [0, -1, 0, 0], "IF" => [1, -1, 1, 0], "ISNA" => [2, 1, 1, 0], "ISERROR" => [3, 1, 1, 0], "SUM" => [4, -1, 0, 0], "AVERAGE" => [5, -1, 0, 0], "MIN" => [6, -1, 0, 0], "MAX" => [7, -1, 0, 0], "ROW" => [8, -1, 0, 0], "COLUMN" => [9, -1, 0, 0], "NA" => [10, 0, 0, 0], "NPV" => [11, -1, 1, 0], "STDEV" => [12, -1, 0, 0], "DOLLAR" => [13, -1, 1, 0], "FIXED" => [14, -1, 1, 0], "SIN" => [15, 1, 1, 0], "COS" => [16, 1, 1, 0], "TAN" => [17, 1, 1, 0], "ATAN" => [18, 1, 1, 0], "PI" => [19, 0, 1, 0], "SQRT" => [20, 1, 1, 0], "EXP" => [21, 1, 1, 0], "LN" => [22, 1, 1, 0], "LOG10" => [23, 1, 1, 0], "ABS" => [24, 1, 1, 0], "INT" => [25, 1, 1, 0], "SIGN" => [26, 1, 1, 0], "ROUND" => [27, 2, 1, 0], "LOOKUP" => [28, -1, 0, 0], "INDEX" => [29, -1, 0, 1], "REPT" => [30, 2, 1, 0], "MID" => [31, 3, 1, 0], "LEN" => [32, 1, 1, 0], "VALUE" => [33, 1, 1, 0], "TRUE" => [34, 0, 1, 0], "FALSE" => [35, 0, 1, 0], "AND" => [36, -1, 0, 0], "OR" => [37, -1, 0, 0], "NOT" => [38, 1, 1, 0], "MOD" => [39, 2, 1, 0], "DCOUNT" => [40, 3, 0, 0], "DSUM" => [41, 3, 0, 0], "DAVERAGE" => [42, 3, 0, 0], "DMIN" => [43, 3, 0, 0], "DMAX" => [44, 3, 0, 0], "DSTDEV" => [45, 3, 0, 0], "VAR" => [46, -1, 0, 0], "DVAR" => [47, 3, 0, 0], "TEXT" => [48, 2, 1, 0], "LINEST" => [49, -1, 0, 0], "TREND" => [50, -1, 0, 0], "LOGEST" => [51, -1, 0, 0], "GROWTH" => [52, -1, 0, 0], "PV" => [56, -1, 1, 0], "FV" => [57, -1, 1, 0], "NPER" => [58, -1, 1, 0], "PMT" => [59, -1, 1, 0], "RATE" => [60, -1, 1, 0], "MIRR" => [61, 3, 0, 0], "IRR" => [62, -1, 0, 0], "RAND" => [63, 0, 1, 1], "MATCH" => [64, -1, 0, 0], "DATE" => [65, 3, 1, 0], "TIME" => [66, 3, 1, 0], "DAY" => [67, 1, 1, 0], "MONTH" => [68, 1, 1, 0], "YEAR" => [69, 1, 1, 0], "WEEKDAY" => [70, -1, 1, 0], "HOUR" => [71, 1, 1, 0], "MINUTE" => [72, 1, 1, 0], "SECOND" => [73, 1, 1, 0], "NOW" => [74, 0, 1, 1], "AREAS" => [75, 1, 0, 1], "ROWS" => [76, 1, 0, 1], "COLUMNS" => [77, 1, 0, 1], "OFFSET" => [78, -1, 0, 1], "SEARCH" => [82, -1, 1, 0], "TRANSPOSE" => [83, 1, 1, 0], "TYPE" => [86, 1, 1, 0], "ATAN2" => [97, 2, 1, 0], "ASIN" => [98, 1, 1, 0], "ACOS" => [99, 1, 1, 0], "CHOOSE" => [100, -1, 1, 0], "HLOOKUP" => [101, -1, 0, 0], "VLOOKUP" => [102, -1, 0, 0], "ISREF" => [105, 1, 0, 0], "LOG" => [109, -1, 1, 0], "CHAR" => [111, 1, 1, 0], "LOWER" => [112, 1, 1, 0], "UPPER" => [113, 1, 1, 0], "PROPER" => [114, 1, 1, 0], "LEFT" => [115, -1, 1, 0], "RIGHT" => [116, -1, 1, 0], "EXACT" => [117, 2, 1, 0], "TRIM" => [118, 1, 1, 0], "REPLACE" => [119, 4, 1, 0], "SUBSTITUTE" => [120, -1, 1, 0], "CODE" => [121, 1, 1, 0], "FIND" => [124, -1, 1, 0], "CELL" => [125, -1, 0, 1], "ISERR" => [126, 1, 1, 0], "ISTEXT" => [127, 1, 1, 0], "ISNUMBER" => [128, 1, 1, 0], "ISBLANK" => [129, 1, 1, 0], "T" => [130, 1, 0, 0], "N" => [131, 1, 0, 0], "DATEVALUE" => [140, 1, 1, 0], "TIMEVALUE" => [141, 1, 1, 0], "SLN" => [142, 3, 1, 0], "SYD" => [143, 4, 1, 0], "DDB" => [144, -1, 1, 0], "INDIRECT" => [148, -1, 1, 1], "CALL" => [150, -1, 1, 0], "CLEAN" => [162, 1, 1, 0], "MDETERM" => [163, 1, 2, 0], "MINVERSE" => [164, 1, 2, 0], "MMULT" => [165, 2, 2, 0], "IPMT" => [167, -1, 1, 0], "PPMT" => [168, -1, 1, 0], "COUNTA" => [169, -1, 0, 0], "PRODUCT" => [183, -1, 0, 0], "FACT" => [184, 1, 1, 0], "DPRODUCT" => [189, 3, 0, 0], "ISNONTEXT" => [190, 1, 1, 0], "STDEVP" => [193, -1, 0, 0], "VARP" => [194, -1, 0, 0], "DSTDEVP" => [195, 3, 0, 0], "DVARP" => [196, 3, 0, 0], "TRUNC" => [197, -1, 1, 0], "ISLOGICAL" => [198, 1, 1, 0], "DCOUNTA" => [199, 3, 0, 0], "USDOLLAR" => [204, -1, 1, 0], "FINDB" => [205, -1, 1, 0], "SEARCHB" => [206, -1, 1, 0], "REPLACEB" => [207, 4, 1, 0], "LEFTB" => [208, -1, 1, 0], "RIGHTB" => [209, -1, 1, 0], "MIDB" => [210, 3, 1, 0], "LENB" => [211, 1, 1, 0], "ROUNDUP" => [212, 2, 1, 0], "ROUNDDOWN" => [213, 2, 1, 0], "ASC" => [214, 1, 1, 0], "DBCS" => [215, 1, 1, 0], "RANK" => [216, -1, 0, 0], "ADDRESS" => [219, -1, 1, 0], "DAYS360" => [220, -1, 1, 0], "TODAY" => [221, 0, 1, 1], "VDB" => [222, -1, 1, 0], "MEDIAN" => [227, -1, 0, 0], "SUMPRODUCT" => [228, -1, 2, 0], "SINH" => [229, 1, 1, 0], "COSH" => [230, 1, 1, 0], "TANH" => [231, 1, 1, 0], "ASINH" => [232, 1, 1, 0], "ACOSH" => [233, 1, 1, 0], "ATANH" => [234, 1, 1, 0], "DGET" => [235, 3, 0, 0], "INFO" => [244, 1, 1, 1], "DB" => [247, -1, 1, 0], "FREQUENCY" => [252, 2, 0, 0], "ERROR.TYPE" => [261, 1, 1, 0], "REGISTER.ID" => [267, -1, 1, 0], "AVEDEV" => [269, -1, 0, 0], "BETADIST" => [270, -1, 1, 0], "GAMMALN" => [271, 1, 1, 0], "BETAINV" => [272, -1, 1, 0], "BINOMDIST" => [273, 4, 1, 0], "CHIDIST" => [274, 2, 1, 0], "CHIINV" => [275, 2, 1, 0], "COMBIN" => [276, 2, 1, 0], "CONFIDENCE" => [277, 3, 1, 0], "CRITBINOM" => [278, 3, 1, 0], "EVEN" => [279, 1, 1, 0], "EXPONDIST" => [280, 3, 1, 0], "FDIST" => [281, 3, 1, 0], "FINV" => [282, 3, 1, 0], "FISHER" => [283, 1, 1, 0], "FISHERINV" => [284, 1, 1, 0], "FLOOR" => [285, 2, 1, 0], "GAMMADIST" => [286, 4, 1, 0], "GAMMAINV" => [287, 3, 1, 0], "CEILING" => [288, 2, 1, 0], "HYPGEOMDIST" => [289, 4, 1, 0], "LOGNORMDIST" => [290, 3, 1, 0], "LOGINV" => [291, 3, 1, 0], "NEGBINOMDIST" => [292, 3, 1, 0], "NORMDIST" => [293, 4, 1, 0], "NORMSDIST" => [294, 1, 1, 0], "NORMINV" => [295, 3, 1, 0], "NORMSINV" => [296, 1, 1, 0], "STANDARDIZE" => [297, 3, 1, 0], "ODD" => [298, 1, 1, 0], "PERMUT" => [299, 2, 1, 0], "POISSON" => [300, 3, 1, 0], "TDIST" => [301, 3, 1, 0], "WEIBULL" => [302, 4, 1, 0], "SUMXMY2" => [303, 2, 2, 0], "SUMX2MY2" => [304, 2, 2, 0], "SUMX2PY2" => [305, 2, 2, 0], "CHITEST" => [306, 2, 2, 0], "CORREL" => [307, 2, 2, 0], "COVAR" => [308, 2, 2, 0], "FORECAST" => [309, 3, 2, 0], "FTEST" => [310, 2, 2, 0], "INTERCEPT" => [311, 2, 2, 0], "PEARSON" => [312, 2, 2, 0], "RSQ" => [313, 2, 2, 0], "STEYX" => [314, 2, 2, 0], "SLOPE" => [315, 2, 2, 0], "TTEST" => [316, 4, 2, 0], "PROB" => [317, -1, 2, 0], "DEVSQ" => [318, -1, 0, 0], "GEOMEAN" => [319, -1, 0, 0], "HARMEAN" => [320, -1, 0, 0], "SUMSQ" => [321, -1, 0, 0], "KURT" => [322, -1, 0, 0], "SKEW" => [323, -1, 0, 0], "ZTEST" => [324, -1, 0, 0], "LARGE" => [325, 2, 0, 0], "SMALL" => [326, 2, 0, 0], "QUARTILE" => [327, 2, 0, 0], "PERCENTILE" => [328, 2, 0, 0], "PERCENTRANK" => [329, -1, 0, 0], "MODE" => [330, -1, 2, 0], "TRIMMEAN" => [331, 2, 0, 0], "TINV" => [332, 2, 1, 0], "CONCATENATE" => [336, -1, 1, 0], "POWER" => [337, 2, 1, 0], "RADIANS" => [342, 1, 1, 0], "DEGREES" => [343, 1, 1, 0], "SUBTOTAL" => [344, -1, 0, 0], "SUMIF" => [345, -1, 0, 0], "COUNTIF" => [346, 2, 0, 0], "COUNTBLANK" => [347, 1, 0, 0], "ISPMT" => [350, 4, 1, 0], "DATEDIF" => [351, 3, 1, 0], "DATESTRING" => [352, 1, 1, 0], "NUMBERSTRING" => [353, 2, 1, 0], "ROMAN" => [354, -1, 1, 0], "GETPIVOTDATA" => [358, -1, 0, 0], "HYPERLINK" => [359, -1, 1, 0], "PHONETIC" => [360, 1, 0, 0], "AVERAGEA" => [361, -1, 0, 0], "MAXA" => [362, -1, 0, 0], "MINA" => [363, -1, 0, 0], "STDEVPA" => [364, -1, 0, 0], "VARPA" => [365, -1, 0, 0], "STDEVA" => [366, -1, 0, 0], "VARA" => [367, -1, 0, 0], "BAHTTEXT" => [368, 1, 0, 0]];
    }
    private function convert($token)
    {
        if(preg_match("/\"([^\"]|\"\"){0,255}\"/", $token)) {
            return $this->convertString($token);
        }
        if(is_numeric($token)) {
            return $this->convertNumber($token);
        }
        if(preg_match("/^\\\$?([A-Ia-i]?[A-Za-z])\\\$?(\\d+)\$/", $token)) {
            return $this->convertRef2d($token);
        }
        if(preg_match("/^[^\\*\\:\\/\\\\\\?\\[\\]\\+\\-\\% \\'\\^\\&\\<\\>\\=\\,\\;\\#\\(\\)\\\"\\{\\}]+(\\:[^\\*\\:\\/\\\\\\?\\[\\]\\+\\-\\% \\'\\^\\&\\<\\>\\=\\,\\;\\#\\(\\)\\\"\\{\\}]+)?\\!\\\$?[A-Ia-i]?[A-Za-z]\\\$?(\\d+)\$/u", $token)) {
            return $this->convertRef3d($token);
        }
        if(preg_match("/^'(([^\\*\\:\\/\\\\\\?\\[\\]\\'])+|(\\'\\')+)+(\\:(([^\\*\\:\\/\\\\\\?\\[\\]\\'])+|(\\'\\')+)+)?'\\!\\\$?[A-Ia-i]?[A-Za-z]\\\$?(\\d+)\$/u", $token)) {
            return $this->convertRef3d($token);
        }
        if(preg_match("/^(\\\$)?[A-Ia-i]?[A-Za-z](\\\$)?(\\d+)\\:(\\\$)?[A-Ia-i]?[A-Za-z](\\\$)?(\\d+)\$/", $token)) {
            return $this->convertRange2d($token);
        }
        if(preg_match("/^[^\\*\\:\\/\\\\\\?\\[\\]\\+\\-\\% \\'\\^\\&\\<\\>\\=\\,\\;\\#\\(\\)\\\"\\{\\}]+(\\:[^\\*\\:\\/\\\\\\?\\[\\]\\+\\-\\% \\'\\^\\&\\<\\>\\=\\,\\;\\#\\(\\)\\\"\\{\\}]+)?\\!\\\$?([A-Ia-i]?[A-Za-z])?\\\$?(\\d+)\\:\\\$?([A-Ia-i]?[A-Za-z])?\\\$?(\\d+)\$/u", $token)) {
            return $this->convertRange3d($token);
        }
        if(preg_match("/^'(([^\\*\\:\\/\\\\\\?\\[\\]\\'])+|(\\'\\')+)+(\\:(([^\\*\\:\\/\\\\\\?\\[\\]\\'])+|(\\'\\')+)+)?'\\!\\\$?([A-Ia-i]?[A-Za-z])?\\\$?(\\d+)\\:\\\$?([A-Ia-i]?[A-Za-z])?\\\$?(\\d+)\$/u", $token)) {
            return $this->convertRange3d($token);
        }
        if(isset($this->ptg[$token])) {
            return pack("C", $this->ptg[$token]);
        }
        if(preg_match("/^#[A-Z0\\/]{3,5}[!?]{1}\$/", $token) || $token == "#N/A") {
            return $this->convertError($token);
        }
        if($token == "arg") {
            return "";
        }
        throw new PHPExcel_Writer_Exception("Unknown token " . $token);
    }
    private function convertNumber($num)
    {
        if(preg_match("/^\\d+\$/", $num) && $num <= 65535) {
            return pack("Cv", $this->ptg["ptgInt"], $num);
        }
        if(PHPExcel_Writer_Excel5_BIFFwriter::getByteOrder()) {
            $num = strrev($num);
        }
        return pack("Cd", $this->ptg["ptgNum"], $num);
    }
    private function convertString($string)
    {
        $string = substr($string, 1, strlen($string) - 2);
        if(255 < strlen($string)) {
            throw new PHPExcel_Writer_Exception("String is too long");
        }
        return pack("C", $this->ptg["ptgStr"]) . PHPExcel_Shared_String::UTF8toBIFF8UnicodeShort($string);
    }
    private function convertFunction($token, $num_args)
    {
        $args = $this->functions[$token][1];
        if(0 <= $args) {
            return pack("Cv", $this->ptg["ptgFuncV"], $this->functions[$token][0]);
        }
        if($args == -1) {
            return pack("CCv", $this->ptg["ptgFuncVarV"], $num_args, $this->functions[$token][0]);
        }
    }
    private function convertRange2d($range, $class = 0)
    {
        if(preg_match("/^(\\\$)?([A-Ia-i]?[A-Za-z])(\\\$)?(\\d+)\\:(\\\$)?([A-Ia-i]?[A-Za-z])(\\\$)?(\\d+)\$/", $range)) {
            list($cell1, $cell2) = explode(":", $range);
            list($row1, $col1) = $this->cellToPackedRowcol($cell1);
            list($row2, $col2) = $this->cellToPackedRowcol($cell2);
            if($class == 0) {
                $ptgArea = pack("C", $this->ptg["ptgArea"]);
            } elseif($class == 1) {
                $ptgArea = pack("C", $this->ptg["ptgAreaV"]);
            } elseif($class == 2) {
                $ptgArea = pack("C", $this->ptg["ptgAreaA"]);
            } else {
                throw new PHPExcel_Writer_Exception("Unknown class " . $class);
            }
            return $ptgArea . $row1 . $row2 . $col1 . $col2;
        }
        throw new PHPExcel_Writer_Exception("Unknown range separator");
    }
    private function convertRange3d($token)
    {
        list($ext_ref, $range) = explode("!", $token);
        $ext_ref = $this->getRefIndex($ext_ref);
        list($cell1, $cell2) = explode(":", $range);
        if(preg_match("/^(\\\$)?[A-Ia-i]?[A-Za-z](\\\$)?(\\d+)\$/", $cell1)) {
            list($row1, $col1) = $this->cellToPackedRowcol($cell1);
            list($row2, $col2) = $this->cellToPackedRowcol($cell2);
        } else {
            list($row1, $col1, $row2, $col2) = $this->rangeToPackedRange($cell1 . ":" . $cell2);
        }
        $ptgArea = pack("C", $this->ptg["ptgArea3d"]);
        return $ptgArea . $ext_ref . $row1 . $row2 . $col1 . $col2;
    }
    private function convertRef2d($cell)
    {
        $cell_array = $this->cellToPackedRowcol($cell);
        list($row, $col) = $cell_array;
        $ptgRef = pack("C", $this->ptg["ptgRefA"]);
        return $ptgRef . $row . $col;
    }
    private function convertRef3d($cell)
    {
        list($ext_ref, $cell) = explode("!", $cell);
        $ext_ref = $this->getRefIndex($ext_ref);
        list($row, $col) = $this->cellToPackedRowcol($cell);
        $ptgRef = pack("C", $this->ptg["ptgRef3dA"]);
        return $ptgRef . $ext_ref . $row . $col;
    }
    private function convertError($errorCode)
    {
        switch ($errorCode) {
            case "#NULL!":
                return pack("C", 0);
                break;
            case "#DIV/0!":
                return pack("C", 7);
                break;
            case "#VALUE!":
                return pack("C", 15);
                break;
            case "#REF!":
                return pack("C", 23);
                break;
            case "#NAME?":
                return pack("C", 29);
                break;
            case "#NUM!":
                return pack("C", 36);
                break;
            case "#N/A":
                return pack("C", 42);
                break;
            default:
                return pack("C", 255);
        }
    }
    private function packExtRef($ext_ref)
    {
        $ext_ref = preg_replace("/^'/", "", $ext_ref);
        $ext_ref = preg_replace("/'\$/", "", $ext_ref);
        if(preg_match("/:/", $ext_ref)) {
            list($sheet_name1, $sheet_name2) = explode(":", $ext_ref);
            $sheet1 = $this->getSheetIndex($sheet_name1);
            if($sheet1 == -1) {
                throw new PHPExcel_Writer_Exception("Unknown sheet name " . $sheet_name1 . " in formula");
            }
            $sheet2 = $this->getSheetIndex($sheet_name2);
            if($sheet2 == -1) {
                throw new PHPExcel_Writer_Exception("Unknown sheet name " . $sheet_name2 . " in formula");
            }
            if($sheet2 < $sheet1) {
                list($sheet1, $sheet2) = [$sheet2, $sheet1];
            }
        } else {
            $sheet1 = $this->getSheetIndex($ext_ref);
            if($sheet1 == -1) {
                throw new PHPExcel_Writer_Exception("Unknown sheet name " . $ext_ref . " in formula");
            }
            $sheet2 = $sheet1;
        }
        $offset = -1 - $sheet1;
        return pack("vdvv", $offset, 0, $sheet1, $sheet2);
    }
    private function getRefIndex($ext_ref)
    {
        $ext_ref = preg_replace("/^'/", "", $ext_ref);
        $ext_ref = preg_replace("/'\$/", "", $ext_ref);
        $ext_ref = str_replace("''", "'", $ext_ref);
        if(preg_match("/:/", $ext_ref)) {
            list($sheet_name1, $sheet_name2) = explode(":", $ext_ref);
            $sheet1 = $this->getSheetIndex($sheet_name1);
            if($sheet1 == -1) {
                throw new PHPExcel_Writer_Exception("Unknown sheet name " . $sheet_name1 . " in formula");
            }
            $sheet2 = $this->getSheetIndex($sheet_name2);
            if($sheet2 == -1) {
                throw new PHPExcel_Writer_Exception("Unknown sheet name " . $sheet_name2 . " in formula");
            }
            if($sheet2 < $sheet1) {
                list($sheet1, $sheet2) = [$sheet2, $sheet1];
            }
        } else {
            $sheet1 = $this->getSheetIndex($ext_ref);
            if($sheet1 == -1) {
                throw new PHPExcel_Writer_Exception("Unknown sheet name " . $ext_ref . " in formula");
            }
            $sheet2 = $sheet1;
        }
        $supbook_index = 0;
        $ref = pack("vvv", $supbook_index, $sheet1, $sheet2);
        $totalreferences = count($this->references);
        $index = -1;
        for ($i = 0; $i < $totalreferences; $i++) {
            if($ref == $this->references[$i]) {
                $index = $i;
                break;
            }
        }
        if($index == -1) {
            $this->references[$totalreferences] = $ref;
            $index = $totalreferences;
        }
        return pack("v", $index);
    }
    private function getSheetIndex($sheet_name)
    {
        if(!isset($this->externalSheets[$sheet_name])) {
            return -1;
        }
        return $this->externalSheets[$sheet_name];
    }
    public function setExtSheet($name, $index)
    {
        $this->externalSheets[$name] = $index;
    }
    private function cellToPackedRowcol($cell)
    {
        $cell = strtoupper($cell);
        list($row, $col, $row_rel, $col_rel) = $this->cellToRowcol($cell);
        if(256 <= $col) {
            throw new PHPExcel_Writer_Exception("Column in: " . $cell . " greater than 255");
        }
        if(65536 <= $row) {
            throw new PHPExcel_Writer_Exception("Row in: " . $cell . " greater than 65536 ");
        }
        $col |= $col_rel << 14;
        $col |= $row_rel << 15;
        $col = pack("v", $col);
        $row = pack("v", $row);
        return [$row, $col];
    }
    private function rangeToPackedRange($range)
    {
        preg_match("/(\\\$)?(\\d+)\\:(\\\$)?(\\d+)/", $range, $match);
        $row1_rel = empty($match[1]) ? 1 : 0;
        $row1 = $match[2];
        $row2_rel = empty($match[3]) ? 1 : 0;
        $row2 = $match[4];
        --$row1;
        --$row2;
        $col1 = 0;
        $col2 = 65535;
        if(65536 <= $row1 || 65536 <= $row2) {
            throw new PHPExcel_Writer_Exception("Row in: " . $range . " greater than 65536 ");
        }
        $col1 |= $row1_rel << 15;
        $col2 |= $row2_rel << 15;
        $col1 = pack("v", $col1);
        $col2 = pack("v", $col2);
        $row1 = pack("v", $row1);
        $row2 = pack("v", $row2);
        return [$row1, $col1, $row2, $col2];
    }
    private function cellToRowcol($cell)
    {
        preg_match("/(\\\$)?([A-I]?[A-Z])(\\\$)?(\\d+)/", $cell, $match);
        $col_rel = empty($match[1]) ? 1 : 0;
        $col_ref = $match[2];
        $row_rel = empty($match[3]) ? 1 : 0;
        $row = $match[4];
        $expn = strlen($col_ref) - 1;
        $col = 0;
        $col_ref_length = strlen($col_ref);
        for ($i = 0; $i < $col_ref_length; $i++) {
            $col += (ord($col_ref[$i]) - 64) * pow(26, $expn);
            --$expn;
        }
        --$row;
        --$col;
        return [$row, $col, $row_rel, $col_rel];
    }
    private function advance()
    {
        $i = $this->currentCharacter;
        $formula_length = strlen($this->formula);
        if($i < $formula_length) {
            while ($this->formula[$i] == " ") {
                $i++;
            }
            if($i < $formula_length - 1) {
                $this->lookAhead = $this->formula[$i + 1];
            }
            $token = "";
        }
        while ($i < $formula_length) {
            $token .= $this->formula[$i];
            if($i < $formula_length - 1) {
                $this->lookAhead = $this->formula[$i + 1];
            } else {
                $this->lookAhead = "";
            }
            if($this->match($token) != "") {
                $this->currentCharacter = $i + 1;
                $this->currentToken = $token;
                return 1;
            }
            if($i < $formula_length - 2) {
                $this->lookAhead = $this->formula[$i + 2];
            } else {
                $this->lookAhead = "";
            }
            $i++;
        }
    }
    private function match($token)
    {
        switch ($token) {
            case "+":
            case "-":
            case "*":
            case "/":
            case "(":
            case ")":
            case ",":
            case ";":
            case ">=":
            case "<=":
            case "=":
            case "<>":
            case "^":
            case "&":
            case "%":
                return $token;
                break;
            case ">":
                if($this->lookAhead == "=") {
                } else {
                    return $token;
                }
                break;
            case "<":
                if($this->lookAhead == "=" || $this->lookAhead == ">") {
                } else {
                    return $token;
                }
                break;
            default:
                if(preg_match("/^\\\$?[A-Ia-i]?[A-Za-z]\\\$?[0-9]+\$/", $token) && !preg_match("/[0-9]/", $this->lookAhead) && $this->lookAhead != ":" && $this->lookAhead != "." && $this->lookAhead != "!") {
                    return $token;
                }
                if(preg_match("/^[^\\*\\:\\/\\\\\\?\\[\\]\\+\\-\\% \\'\\^\\&\\<\\>\\=\\,\\;\\#\\(\\)\\\"\\{\\}]+(\\:[^\\*\\:\\/\\\\\\?\\[\\]\\+\\-\\% \\'\\^\\&\\<\\>\\=\\,\\;\\#\\(\\)\\\"\\{\\}]+)?\\!\\\$?[A-Ia-i]?[A-Za-z]\\\$?[0-9]+\$/u", $token) && !preg_match("/[0-9]/", $this->lookAhead) && $this->lookAhead != ":" && $this->lookAhead != ".") {
                    return $token;
                }
                if(preg_match("/^'(([^\\*\\:\\/\\\\\\?\\[\\]\\'])+|(\\'\\')+)+(\\:(([^\\*\\:\\/\\\\\\?\\[\\]\\'])+|(\\'\\')+)+)?'\\!\\\$?[A-Ia-i]?[A-Za-z]\\\$?[0-9]+\$/u", $token) && !preg_match("/[0-9]/", $this->lookAhead) && $this->lookAhead != ":" && $this->lookAhead != ".") {
                    return $token;
                }
                if(preg_match("/^(\\\$)?[A-Ia-i]?[A-Za-z](\\\$)?[0-9]+:(\\\$)?[A-Ia-i]?[A-Za-z](\\\$)?[0-9]+\$/", $token) && !preg_match("/[0-9]/", $this->lookAhead)) {
                    return $token;
                }
                if(preg_match("/^[^\\*\\:\\/\\\\\\?\\[\\]\\+\\-\\% \\'\\^\\&\\<\\>\\=\\,\\;\\#\\(\\)\\\"\\{\\}]+(\\:[^\\*\\:\\/\\\\\\?\\[\\]\\+\\-\\% \\'\\^\\&\\<\\>\\=\\,\\;\\#\\(\\)\\\"\\{\\}]+)?\\!\\\$?([A-Ia-i]?[A-Za-z])?\\\$?[0-9]+:\\\$?([A-Ia-i]?[A-Za-z])?\\\$?[0-9]+\$/u", $token) && !preg_match("/[0-9]/", $this->lookAhead)) {
                    return $token;
                }
                if(preg_match("/^'(([^\\*\\:\\/\\\\\\?\\[\\]\\'])+|(\\'\\')+)+(\\:(([^\\*\\:\\/\\\\\\?\\[\\]\\'])+|(\\'\\')+)+)?'\\!\\\$?([A-Ia-i]?[A-Za-z])?\\\$?[0-9]+:\\\$?([A-Ia-i]?[A-Za-z])?\\\$?[0-9]+\$/u", $token) && !preg_match("/[0-9]/", $this->lookAhead)) {
                    return $token;
                }
                if(is_numeric($token) && (!is_numeric($token . $this->lookAhead) || $this->lookAhead == "") && $this->lookAhead != "!" && $this->lookAhead != ":") {
                    return $token;
                }
                if(preg_match("/\"([^\"]|\"\"){0,255}\"/", $token) && $this->lookAhead != "\"" && substr_count($token, "\"") % 2 == 0) {
                    return $token;
                }
                if(preg_match("/^#[A-Z0\\/]{3,5}[!?]{1}\$/", $token) || $token == "#N/A") {
                    return $token;
                }
                if(preg_match("/^[A-Z0-9�-�\\.]+\$/i", $token) && $this->lookAhead == "(") {
                    return $token;
                }
                if(substr($token, -1) == ")") {
                    return $token;
                }
                return "";
        }
    }
    public function parse($formula)
    {
        $this->currentCharacter = 0;
        $this->formula = $formula;
        $this->lookAhead = isset($formula[1]) ? $formula[1] : "";
        $this->advance();
        $this->parseTree = $this->condition();
        return true;
    }
    private function condition()
    {
        $result = $this->expression();
        if($this->currentToken == "<") {
            $this->advance();
            $result2 = $this->expression();
            $result = $this->createTree("ptgLT", $result, $result2);
        } elseif($this->currentToken == ">") {
            $this->advance();
            $result2 = $this->expression();
            $result = $this->createTree("ptgGT", $result, $result2);
        } elseif($this->currentToken == "<=") {
            $this->advance();
            $result2 = $this->expression();
            $result = $this->createTree("ptgLE", $result, $result2);
        } elseif($this->currentToken == ">=") {
            $this->advance();
            $result2 = $this->expression();
            $result = $this->createTree("ptgGE", $result, $result2);
        } elseif($this->currentToken == "=") {
            $this->advance();
            $result2 = $this->expression();
            $result = $this->createTree("ptgEQ", $result, $result2);
        } elseif($this->currentToken == "<>") {
            $this->advance();
            $result2 = $this->expression();
            $result = $this->createTree("ptgNE", $result, $result2);
        } elseif($this->currentToken == "&") {
            $this->advance();
            $result2 = $this->expression();
            $result = $this->createTree("ptgConcat", $result, $result2);
        }
        return $result;
    }
    private function expression()
    {
        if(preg_match("/\"([^\"]|\"\"){0,255}\"/", $this->currentToken)) {
            $tmp = str_replace("\"\"", "\"", $this->currentToken);
            if($tmp == "\"" || $tmp == "") {
                $tmp = "\"\"";
            }
            $result = $this->createTree($tmp, "", "");
            $this->advance();
            return $result;
        }
        if(preg_match("/^#[A-Z0\\/]{3,5}[!?]{1}\$/", $this->currentToken) || $this->currentToken == "#N/A") {
            $result = $this->createTree($this->currentToken, "ptgErr", "");
            $this->advance();
            return $result;
        }
        if($this->currentToken == "-") {
            $this->advance();
            $result2 = $this->expression();
            $result = $this->createTree("ptgUminus", $result2, "");
            return $result;
        }
        if($this->currentToken == "+") {
            $this->advance();
            $result2 = $this->expression();
            $result = $this->createTree("ptgUplus", $result2, "");
            return $result;
        }
        $result = $this->term();
        while ($this->currentToken == "+" || $this->currentToken == "-" || $this->currentToken == "^") {
            if($this->currentToken == "+") {
                $this->advance();
                $result2 = $this->term();
                $result = $this->createTree("ptgAdd", $result, $result2);
            } elseif($this->currentToken == "-") {
                $this->advance();
                $result2 = $this->term();
                $result = $this->createTree("ptgSub", $result, $result2);
            } else {
                $this->advance();
                $result2 = $this->term();
                $result = $this->createTree("ptgPower", $result, $result2);
            }
        }
        return $result;
    }
    private function parenthesizedExpression()
    {
        $result = $this->createTree("ptgParen", $this->expression(), "");
        return $result;
    }
    private function term()
    {
        $result = $this->fact();
        while ($this->currentToken == "*" || $this->currentToken == "/") {
            if($this->currentToken == "*") {
                $this->advance();
                $result2 = $this->fact();
                $result = $this->createTree("ptgMul", $result, $result2);
            } else {
                $this->advance();
                $result2 = $this->fact();
                $result = $this->createTree("ptgDiv", $result, $result2);
            }
        }
        return $result;
    }
    private function fact()
    {
        if($this->currentToken == "(") {
            $this->advance();
            $result = $this->parenthesizedExpression();
            if($this->currentToken != ")") {
                throw new PHPExcel_Writer_Exception("')' token expected.");
            }
            $this->advance();
            return $result;
        }
        if(preg_match("/^\\\$?[A-Ia-i]?[A-Za-z]\\\$?[0-9]+\$/", $this->currentToken)) {
            $result = $this->createTree($this->currentToken, "", "");
            $this->advance();
            return $result;
        }
        if(preg_match("/^[^\\*\\:\\/\\\\\\?\\[\\]\\+\\-\\% \\'\\^\\&\\<\\>\\=\\,\\;\\#\\(\\)\\\"\\{\\}]+(\\:[^\\*\\:\\/\\\\\\?\\[\\]\\+\\-\\% \\'\\^\\&\\<\\>\\=\\,\\;\\#\\(\\)\\\"\\{\\}]+)?\\!\\\$?[A-Ia-i]?[A-Za-z]\\\$?[0-9]+\$/u", $this->currentToken)) {
            $result = $this->createTree($this->currentToken, "", "");
            $this->advance();
            return $result;
        }
        if(preg_match("/^'(([^\\*\\:\\/\\\\\\?\\[\\]\\'])+|(\\'\\')+)+(\\:(([^\\*\\:\\/\\\\\\?\\[\\]\\'])+|(\\'\\')+)+)?'\\!\\\$?[A-Ia-i]?[A-Za-z]\\\$?[0-9]+\$/u", $this->currentToken)) {
            $result = $this->createTree($this->currentToken, "", "");
            $this->advance();
            return $result;
        }
        if(preg_match("/^(\\\$)?[A-Ia-i]?[A-Za-z](\\\$)?[0-9]+:(\\\$)?[A-Ia-i]?[A-Za-z](\\\$)?[0-9]+\$/", $this->currentToken) || preg_match("/^(\\\$)?[A-Ia-i]?[A-Za-z](\\\$)?[0-9]+\\.\\.(\\\$)?[A-Ia-i]?[A-Za-z](\\\$)?[0-9]+\$/", $this->currentToken)) {
            $result = $this->createTree($this->currentToken, "", "");
            $this->advance();
            return $result;
        }
        if(preg_match("/^[^\\*\\:\\/\\\\\\?\\[\\]\\+\\-\\% \\'\\^\\&\\<\\>\\=\\,\\;\\#\\(\\)\\\"\\{\\}]+(\\:[^\\*\\:\\/\\\\\\?\\[\\]\\+\\-\\% \\'\\^\\&\\<\\>\\=\\,\\;\\#\\(\\)\\\"\\{\\}]+)?\\!\\\$?([A-Ia-i]?[A-Za-z])?\\\$?[0-9]+:\\\$?([A-Ia-i]?[A-Za-z])?\\\$?[0-9]+\$/u", $this->currentToken)) {
            $result = $this->createTree($this->currentToken, "", "");
            $this->advance();
            return $result;
        }
        if(preg_match("/^'(([^\\*\\:\\/\\\\\\?\\[\\]\\'])+|(\\'\\')+)+(\\:(([^\\*\\:\\/\\\\\\?\\[\\]\\'])+|(\\'\\')+)+)?'\\!\\\$?([A-Ia-i]?[A-Za-z])?\\\$?[0-9]+:\\\$?([A-Ia-i]?[A-Za-z])?\\\$?[0-9]+\$/u", $this->currentToken)) {
            $result = $this->createTree($this->currentToken, "", "");
            $this->advance();
            return $result;
        }
        if(is_numeric($this->currentToken)) {
            if($this->lookAhead == "%") {
                $result = $this->createTree("ptgPercent", $this->currentToken, "");
                $this->advance();
            } else {
                $result = $this->createTree($this->currentToken, "", "");
            }
            $this->advance();
            return $result;
        }
        if(preg_match("/^[A-Z0-9�-�\\.]+\$/i", $this->currentToken)) {
            $result = $this->func();
            return $result;
        }
        throw new PHPExcel_Writer_Exception("Syntax error: " . $this->currentToken . ", lookahead: " . $this->lookAhead . ", current char: " . $this->currentCharacter);
    }
    private function func()
    {
        $num_args = 0;
        $function = strtoupper($this->currentToken);
        $result = "";
        $this->advance();
        $this->advance();
        while ($this->currentToken != ")") {
            if(0 < $num_args) {
                if($this->currentToken == "," || $this->currentToken == ";") {
                    $this->advance();
                    $result2 = $this->condition();
                    $result = $this->createTree("arg", $result, $result2);
                } else {
                    throw new PHPExcel_Writer_Exception("Syntax error: comma expected in function " . $function . ", arg #" . $num_args);
                }
            } else {
                $result2 = $this->condition();
                $result = $this->createTree("arg", "", $result2);
            }
            $num_args++;
        }
        if(!isset($this->functions[$function])) {
            throw new PHPExcel_Writer_Exception("Function " . $function . "() doesn't exist");
        }
        $args = $this->functions[$function][1];
        if(0 <= $args && $args != $num_args) {
            throw new PHPExcel_Writer_Exception("Incorrect number of arguments in function " . $function . "() ");
        }
        $result = $this->createTree($function, $result, $num_args);
        $this->advance();
        return $result;
    }
    private function createTree($value, $left, $right)
    {
        return ["value" => $value, "left" => $left, "right" => $right];
    }
    public function toReversePolish($tree = [])
    {
        $polish = "";
        if(empty($tree)) {
            $tree = $this->parseTree;
        }
        if(is_array($tree["left"])) {
            $converted_tree = $this->toReversePolish($tree["left"]);
            $polish .= $converted_tree;
        } elseif($tree["left"] != "") {
            $converted_tree = $this->convert($tree["left"]);
            $polish .= $converted_tree;
        }
        if(is_array($tree["right"])) {
            $converted_tree = $this->toReversePolish($tree["right"]);
            $polish .= $converted_tree;
        } elseif($tree["right"] != "") {
            $converted_tree = $this->convert($tree["right"]);
            $polish .= $converted_tree;
        }
        if(preg_match("/^[A-Z0-9�-�\\.]+\$/", $tree["value"]) && !preg_match("/^([A-Ia-i]?[A-Za-z])(\\d+)\$/", $tree["value"]) && !preg_match("/^[A-Ia-i]?[A-Za-z](\\d+)\\.\\.[A-Ia-i]?[A-Za-z](\\d+)\$/", $tree["value"]) && !is_numeric($tree["value"]) && !isset($this->ptg[$tree["value"]])) {
            if($tree["left"] != "") {
                $left_tree = $this->toReversePolish($tree["left"]);
            } else {
                $left_tree = "";
            }
            return $left_tree . $this->convertFunction($tree["value"], $tree["right"]);
        }
        $converted_tree = $this->convert($tree["value"]);
        $polish .= $converted_tree;
        return $polish;
    }
}

?>